// This is free and unencumbered software released into the public domain.

// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.

// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

// For more information, please refer to <http://unlicense.org/>

/**
 * @file incbin.h
 * @author Dale Weiler
 * @brief Utility for including binary files
 *
 * Facilities for including binary files into the current translation unit and
 * making use from them externally in other translation units.
 */
#ifndef INCBIN_HDR
#define INCBIN_HDR

#if defined(_MSC_VER) && !defined(__clang__)
#define INCBIN_MSC_VER _MSC_VER
#endif

#include <limits.h>
#if defined(__AVX512BW__) || defined(__AVX512CD__) || defined(__AVX512DQ__) || defined(__AVX512ER__) ||      \
    defined(__AVX512PF__) || defined(__AVX512VL__) || defined(__AVX512F__)
#define INCBIN_ALIGNMENT_INDEX 6
#elif defined(__AVX__) || defined(__AVX2__)
#define INCBIN_ALIGNMENT_INDEX 5
#elif defined(__SSE__) || defined(__SSE2__) || defined(__SSE3__) || defined(__SSSE3__) ||                    \
    defined(__SSE4_1__) || defined(__SSE4_2__) || defined(__neon__) || defined(__ARM_NEON) ||                \
    defined(__ALTIVEC__)
#define INCBIN_ALIGNMENT_INDEX 4
#elif ULONG_MAX != 0xffffffffu
#define INCBIN_ALIGNMENT_INDEX 3
#else
#define INCBIN_ALIGNMENT_INDEX 2
#endif

/* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */
#define INCBIN_ALIGN_SHIFT_0 1
#define INCBIN_ALIGN_SHIFT_1 2
#define INCBIN_ALIGN_SHIFT_2 4
#define INCBIN_ALIGN_SHIFT_3 8
#define INCBIN_ALIGN_SHIFT_4 16
#define INCBIN_ALIGN_SHIFT_5 32
#define INCBIN_ALIGN_SHIFT_6 64

/* Actual alignment value */
#define INCBIN_ALIGNMENT INCBIN_CONCATENATE(INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), INCBIN_ALIGNMENT_INDEX)

/* Stringize */
#define INCBIN_STR(X) #X
#define INCBIN_STRINGIZE(X) INCBIN_STR(X)
/* Concatenate */
#define INCBIN_CAT(X, Y) X##Y
#define INCBIN_CONCATENATE(X, Y) INCBIN_CAT(X, Y)
/* Deferred macro expansion */
#define INCBIN_EVAL(X) X
#define INCBIN_INVOKE(N, ...) INCBIN_EVAL(N(__VA_ARGS__))
/* Variable argument count for overloading by arity */
#define INCBIN_VA_ARG_COUNTER(_1, _2, _3, N, ...) N
#define INCBIN_VA_ARGC(...) INCBIN_VA_ARG_COUNTER(__VA_ARGS__, 3, 2, 1, 0)

/* Green Hills uses a different directive for including binary data */
#if defined(__ghs__)
#if (__ghs_asm == 2)
#define INCBIN_MACRO ".file"
/* Or consider the ".myrawdata" entry in the ld file */
#else
#define INCBIN_MACRO "\tINCBIN"
#endif
#else
#define INCBIN_MACRO ".incbin"
#endif

#ifndef INCBIN_MSC_VER
#define INCBIN_ALIGN __attribute__((aligned(INCBIN_ALIGNMENT)))
#else
#define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT))
#endif

#if defined(__arm__) || /* GNU C and RealView */                                                             \
    defined(__arm) ||   /* Diab */                                                                           \
    defined(_ARM)       /* ImageCraft */
#define INCBIN_ARM
#endif

#ifdef __GNUC__
/* Utilize .balign where supported */
#define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
#define INCBIN_ALIGN_BYTE ".balign 1\n"
#elif defined(INCBIN_ARM)
/*
 * On arm assemblers, the alignment value is calculated as (1 << n) where `n' is
 * the shift count. This is the value passed to `.align'
 */
#define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n"
#define INCBIN_ALIGN_BYTE ".align 0\n"
#else
/* We assume other inline assembler's treat `.align' as `.balign' */
#define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
#define INCBIN_ALIGN_BYTE ".align 1\n"
#endif

/* INCBIN_CONST is used by incbin.c generated files */
#if defined(__cplusplus)
#define INCBIN_EXTERNAL extern "C"
#define INCBIN_CONST extern const
#else
#define INCBIN_EXTERNAL extern
#define INCBIN_CONST const
#endif

/**
 * @brief Optionally override the linker section into which size and data is
 * emitted.
 *
 * @warning If you use this facility, you might have to deal with
 * platform-specific linker output section naming on your own.
 */
#if !defined(INCBIN_OUTPUT_SECTION)
#if defined(__APPLE__)
#define INCBIN_OUTPUT_SECTION ".const_data"
#else
#define INCBIN_OUTPUT_SECTION ".rodata"
#endif
#endif

/**
 * @brief Optionally override the linker section into which data is emitted.
 *
 * @warning If you use this facility, you might have to deal with
 * platform-specific linker output section naming on your own.
 */
#if !defined(INCBIN_OUTPUT_DATA_SECTION)
#define INCBIN_OUTPUT_DATA_SECTION INCBIN_OUTPUT_SECTION
#endif

/**
 * @brief Optionally override the linker section into which size is emitted.
 *
 * @warning If you use this facility, you might have to deal with
 * platform-specific linker output section naming on your own.
 *
 * @note This is useful for Harvard architectures where program memory cannot
 * be directly read from the program without special instructions. With this you
 * can chose to put the size variable in RAM rather than ROM.
 */
#if !defined(INCBIN_OUTPUT_SIZE_SECTION)
#define INCBIN_OUTPUT_SIZE_SECTION INCBIN_OUTPUT_SECTION
#endif

#if defined(__APPLE__)
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE && !defined(INCBIN_SILENCE_BITCODE_WARNING)
#warning                                                                                                     \
    "incbin is incompatible with bitcode. Using the library will break upload to App Store if you have bitcode enabled. Add `#define INCBIN_SILENCE_BITCODE_WARNING` before including this header to silence this warning."
#endif
/* The directives are different for Apple branded compilers */
#define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
#define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
#define INCBIN_INT ".long "
#define INCBIN_MANGLE "_"
#define INCBIN_BYTE ".byte "
#define INCBIN_TYPE(...)
#else
#define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n"
#define INCBIN_GLOBAL(NAME) ".global " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
#if defined(__ghs__)
#define INCBIN_INT ".word "
#else
#define INCBIN_INT ".int "
#endif
#if defined(__USER_LABEL_PREFIX__)
#define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__)
#else
#define INCBIN_MANGLE ""
#endif
#if defined(INCBIN_ARM)
/* On arm assemblers, `@' is used as a line comment token */
#define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n"
#elif defined(__MINGW32__) || defined(__MINGW64__) || (defined(_MSC_VER) && defined(__clang__))
/* Mingw doesn't support this directive either */
#define INCBIN_TYPE(NAME)
#else
/* It's safe to use `@' on other architectures */
#define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n"
#endif
#define INCBIN_BYTE ".byte "
#endif

/* List of style types used for symbol names */
#define INCBIN_STYLE_CAMEL 0
#define INCBIN_STYLE_SNAKE 1

/**
 * @brief Specify the prefix to use for symbol names.
 *
 * @note By default this is "g".
 *
 * @code
 * #define INCBIN_PREFIX incbin
 * #include "incbin.h"
 * INCBIN(Foo, "foo.txt");
 *
 * // Now you have the following symbols instead:
 * // const unsigned char incbinFoo<data>[];
 * // const unsigned char *const incbinFoo<end>;
 * // const unsigned int incbinFoo<size>;
 * @endcode
 */
#if !defined(INCBIN_PREFIX)
#define INCBIN_PREFIX rsrc__
#endif

/**
 * @brief Specify the style used for symbol names.
 *
 * Possible options are
 * - INCBIN_STYLE_CAMEL "CamelCase"
 * - INCBIN_STYLE_SNAKE "snake_case"
 *
 * @note By default this is INCBIN_STYLE_CAMEL
 *
 * @code
 * #define INCBIN_STYLE INCBIN_STYLE_SNAKE
 * #include "incbin.h"
 * INCBIN(foo, "foo.txt");
 *
 * // Now you have the following symbols:
 * // const unsigned char <prefix>foo_data[];
 * // const unsigned char *const <prefix>foo_end;
 * // const unsigned int <prefix>foo_size;
 * @endcode
 */
#if !defined(INCBIN_STYLE)
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
#endif

/* Style lookup tables */
#define INCBIN_STYLE_0_DATA Data
#define INCBIN_STYLE_0_END End
#define INCBIN_STYLE_0_SIZE Size
#define INCBIN_STYLE_1_DATA _data
#define INCBIN_STYLE_1_END _end
#define INCBIN_STYLE_1_SIZE _size

/* Style lookup: returning identifier */
#define INCBIN_STYLE_IDENT(TYPE)                                                                             \
    INCBIN_CONCATENATE(INCBIN_STYLE_,                                                                        \
                       INCBIN_CONCATENATE(INCBIN_EVAL(INCBIN_STYLE), INCBIN_CONCATENATE(_, TYPE)))

/* Style lookup: returning string literal */
#define INCBIN_STYLE_STRING(TYPE) INCBIN_STRINGIZE(INCBIN_STYLE_IDENT(TYPE))

/* Generate the global labels by indirectly invoking the macro with our style
 * type and concatenating the name against them. */
#define INCBIN_GLOBAL_LABELS(NAME, TYPE)                                                                     \
    INCBIN_INVOKE(INCBIN_GLOBAL, INCBIN_CONCATENATE(NAME, INCBIN_INVOKE(INCBIN_STYLE_IDENT, TYPE)))          \
    INCBIN_INVOKE(INCBIN_TYPE, INCBIN_CONCATENATE(NAME, INCBIN_INVOKE(INCBIN_STYLE_IDENT, TYPE)))

/**
 * @brief Externally reference binary data included in another translation unit.
 *
 * Produces three external symbols that reference the binary data included in
 * another translation unit.
 *
 * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
 * "Data", as well as "End" and "Size" after. An example is provided below.
 *
 * @param TYPE Optional array type. Omitting this picks a default of `unsigned char`.
 * @param NAME The name given for the binary data
 *
 * @code
 * INCBIN_EXTERN(Foo);
 *
 * // Now you have the following symbols:
 * // extern const unsigned char <prefix>Foo<data>[];
 * // extern const unsigned char *const <prefix>Foo<end>;
 * // extern const unsigned int <prefix>Foo<size>;
 * @endcode
 *
 * You may specify a custom optional data type as well as the first argument.
 * @code
 * INCBIN_EXTERN(custom_type, Foo);
 *
 * // Now you have the following symbols:
 * // extern const custom_type <prefix>Foo<data>[];
 * // extern const custom_type *const <prefix>Foo<end>;
 * // extern const unsigned int <prefix>Foo<size>;
 * @endcode
 */
#define INCBIN_EXTERN(...) INCBIN_CONCATENATE(INCBIN_EXTERN_, INCBIN_VA_ARGC(__VA_ARGS__))(__VA_ARGS__)
#define INCBIN_EXTERN_1(NAME, ...) INCBIN_EXTERN_2(unsigned char, NAME)
#define INCBIN_EXTERN_2(TYPE, NAME)                                                                          \
    INCBIN_EXTERNAL const INCBIN_ALIGN TYPE INCBIN_CONCATENATE(INCBIN_CONCATENATE(INCBIN_PREFIX, NAME),      \
                                                               INCBIN_STYLE_IDENT(DATA))[];                  \
    INCBIN_EXTERNAL const INCBIN_ALIGN TYPE* const INCBIN_CONCATENATE(                                       \
        INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), INCBIN_STYLE_IDENT(END));                                   \
    INCBIN_EXTERNAL const unsigned int INCBIN_CONCATENATE(INCBIN_CONCATENATE(INCBIN_PREFIX, NAME),           \
                                                          INCBIN_STYLE_IDENT(SIZE))

/**
 * @brief Externally reference textual data included in another translation unit.
 *
 * Produces three external symbols that reference the textual data included in
 * another translation unit.
 *
 * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
 * "Data", as well as "End" and "Size" after. An example is provided below.
 *
 * @param NAME The name given for the textual data
 *
 * @code
 * INCBIN_EXTERN(Foo);
 *
 * // Now you have the following symbols:
 * // extern const char <prefix>Foo<data>[];
 * // extern const char *const <prefix>Foo<end>;
 * // extern const unsigned int <prefix>Foo<size>;
 * @endcode
 */
#define INCTXT_EXTERN(NAME) INCBIN_EXTERN_2(char, NAME)

/**
 * @brief Include a binary file into the current translation unit.
 *
 * Includes a binary file into the current translation unit, producing three symbols
 * for objects that encode the data and size respectively.
 *
 * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
 * "Data", as well as "End" and "Size" after. An example is provided below.
 *
 * @param TYPE Optional array type. Omitting this picks a default of `unsigned char`.
 * @param NAME The name to associate with this binary data (as an identifier.)
 * @param FILENAME The file to include (as a string literal.)
 *
 * @code
 * INCBIN(Icon, "icon.png");
 *
 * // Now you have the following symbols:
 * // const unsigned char <prefix>Icon<data>[];
 * // const unsigned char *const <prefix>Icon<end>;
 * // const unsigned int <prefix>Icon<size>;
 * @endcode
 *
 * You may specify a custom optional data type as well as the first argument.
 * These macros are specialized by arity.
 * @code
 * INCBIN(custom_type, Icon, "icon.png");
 *
 * // Now you have the following symbols:
 * // const custom_type <prefix>Icon<data>[];
 * // const custom_type *const <prefix>Icon<end>;
 * // const unsigned int <prefix>Icon<size>;
 * @endcode
 *
 * @warning This must be used in global scope
 * @warning The identifiers may be different if INCBIN_STYLE is not default
 *
 * To externally reference the data included by this in another translation unit
 * please @see INCBIN_EXTERN.
 */
#ifdef INCBIN_MSC_VER
#define INCBIN(NAME, FILENAME) INCBIN_EXTERN(NAME)
#else
#define INCBIN(...) INCBIN_CONCATENATE(INCBIN_, INCBIN_VA_ARGC(__VA_ARGS__))(__VA_ARGS__)
#if defined(__GNUC__)
#define INCBIN_1(...) _Pragma("GCC error \"Single argument INCBIN not allowed\"")
#elif defined(__clang__)
#define INCBIN_1(...) _Pragma("clang error \"Single argument INCBIN not allowed\"")
#else
#define INCBIN_1(...) /* Cannot do anything here */
#endif
#define INCBIN_2(NAME, FILENAME) INCBIN_3(unsigned char, NAME, FILENAME)
#define INCBIN_3(TYPE, NAME, FILENAME)                                                                       \
    INCBIN_COMMON(TYPE, NAME, FILENAME, /* No terminator for binary data */)
#define INCBIN_COMMON(TYPE, NAME, FILENAME, TERMINATOR)                                                      \
    __asm__(                                                                                                 \
        INCBIN_SECTION INCBIN_GLOBAL_LABELS(NAME, DATA)                                                      \
            INCBIN_ALIGN_HOST INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(       \
                DATA) ":\n" INCBIN_MACRO " \"" FILENAME "\"\n" TERMINATOR INCBIN_GLOBAL_LABELS(NAME, END)    \
                INCBIN_ALIGN_BYTE INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(   \
                    END) ":\n" INCBIN_BYTE "1\n" INCBIN_GLOBAL_LABELS(NAME, SIZE)                            \
                    INCBIN_ALIGN_HOST INCBIN_MANGLE INCBIN_STRINGIZE(                                        \
                        INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" INCBIN_INT INCBIN_MANGLE        \
                        INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " INCBIN_MANGLE   \
                            INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(                       \
                                DATA) "\n" INCBIN_ALIGN_HOST ".text\n");                                     \
    INCBIN_EXTERN(TYPE, NAME)
#endif

/**
 * @brief Include a textual file into the current translation unit.
 *
 * This behaves the same as INCBIN except it produces char compatible arrays
 * and implicitly adds a null-terminator byte, thus the size of data included
 * by this is one byte larger than that of INCBIN.
 *
 * Includes a textual file into the current translation unit, producing three
 * symbols for objects that encode the data and size respectively.
 *
 * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
 * "Data", as well as "End" and "Size" after. An example is provided below.
 *
 * @param NAME The name to associate with this binary data (as an identifier.)
 * @param FILENAME The file to include (as a string literal.)
 *
 * @code
 * INCTXT(Readme, "readme.txt");
 *
 * // Now you have the following symbols:
 * // const char <prefix>Readme<data>[];
 * // const char *const <prefix>Readme<end>;
 * // const unsigned int <prefix>Readme<size>;
 * @endcode
 *
 * @warning This must be used in global scope
 * @warning The identifiers may be different if INCBIN_STYLE is not default
 *
 * To externally reference the data included by this in another translation unit
 * please @see INCBIN_EXTERN.
 */
#if defined(INCBIN_MSC_VER)
#define INCTXT(NAME, FILENAME) INCBIN_EXTERN(NAME)
#else
#define INCTXT(NAME, FILENAME) INCBIN_COMMON(char, NAME, FILENAME, INCBIN_BYTE "0\n")
#endif

#endif
